
package kalkon;

import java.util.logging.Level;
import java.util.logging.Logger;
import kalkon.outputModules.OutputModuleFTP;
import kalkon.outputModules.OutputModuleLocal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import nu.xom.Document;
import nu.xom.Text;

/**
 * Main class of Calcon project.
 * 
 * @author tommz9 tommz9@gmail.com
 */
public class main {

    public static String defaultConfLocation = "conf.xml";
    public static int SUCCESS = 0;
    public static int FAILURE = 1;

    private enum ArgState {
        DEFAULT, CONF_NEXT;
    }

    private static int IO_BUFFER_SIZE = 100;

    private static void copy(InputStream in, OutputStream out) throws IOException {
        byte[] b = new byte[IO_BUFFER_SIZE];
        int read;
        while ((read = in.read(b)) != -1) {
            out.write(b, 0, read);
        }
    }

    public static void main(String[] args) {

        // Localization
        ResourceBundle bundle = ResourceBundle.getBundle("kalkon.messages");

        String confLocation = defaultConfLocation;

        // Check input parameters

        ArgState state = ArgState.DEFAULT;
        for(int i =0 ; i < args.length ;i++){
            String arg = args[i];

            switch(state){
                case DEFAULT:
                    if(arg.equals("--configuration")){
                        state = ArgState.CONF_NEXT;
                    } else {
                        System.err.println(
                            MessageFormat.format(
                                bundle.getString("unknown.argument"), arg));
                        System.exit(FAILURE);
                    }
                    break;
                case CONF_NEXT:
                    confLocation = arg;
                    state = ArgState.DEFAULT;
                    break;
            }

        }

        // check, if something is not missing
        switch(state){
            case DEFAULT:
                // OK
                break;
            case CONF_NEXT:
                System.err.println(MessageFormat.format(
                        bundle.getString("param.missing"),
                        "--configuration"));
                System.exit(FAILURE);
                break;
        }

        System.out.println(bundle.getString("loading.conf"));
        // Load configuration file
        File configFile = new File(confLocation);
        if (!configFile.exists()) {
            try {
                // create new default config file
                configFile.createNewFile();
                if (configFile.canWrite()) {
                    OutputStream configStream = new FileOutputStream(configFile);
                    InputStream defaultConfigStream = configFile.getClass()
                            .getResourceAsStream("/kalkon/defaultFiles/defaultConfig.xml");

                    if (defaultConfigStream != null) {
                        copy(defaultConfigStream, configStream);
                        defaultConfigStream.close();
                        System.out.println(MessageFormat.format(
                                bundle.getString("default.conf.created"),
                                configFile.getPath()));
                    }
                    configStream.flush();
                    configStream.close();
                }

            } catch (IOException ex) {
                System.err.println(bundle.getString("cannot.create.default.conf"));
                System.err.println(ex);
                System.exit(FAILURE);
            }
        }
        
        InputStream confStream = null;
        try {
            confStream = new FileInputStream(confLocation);
        } catch (FileNotFoundException ex) {
            System.err.println(MessageFormat.format(bundle.getString("cannot.open.conf")
                    , confLocation));
            System.exit(FAILURE);
        }

        Configuration conf = (new ConfigurationLoader()).loadConfiguration(confStream);

        if(conf == null){
            // error message was printed
            System.exit(FAILURE);
        }

        // load input modules

        System.out.println(bundle.getString("loading.input.modules"));

        if(conf.getInputModules().isEmpty()){
            System.err.println(bundle.getString("no.input.modules"));
            System.exit(FAILURE);
        }

        List<InputModule> inputModules = new ArrayList<InputModule>
                (conf.getInputModules().size());
        InputModuleLoader moduleLoader = new InputModuleLoader();

        for(String key : conf.getInputModules().keySet()){
            String value = conf.getInputModules().get(key);

            System.out.println(MessageFormat.format(
                    bundle.getString("loading.one.module"),
                    value, key));

            InputModule im = moduleLoader.loadModule(value, key);
            if(im == null){
                // message was printed
                System.exit(FAILURE);
            }
            inputModules.add(im);
        }

        // Load internal representation file

        System.out.println(MessageFormat.format(
                bundle.getString("loading.database"),
                conf.getDatabaseLocation()));

        File database = new File(conf.getDatabaseLocation());

        if(!database.exists()){
            System.out.printf(MessageFormat.format(
                    bundle.getString("cannot.find.database"),
                    conf.getDatabaseLocation()));
            try {
                database.createNewFile();

                // copy empty database to created file
                OutputStream databaseStream = new FileOutputStream(database);
                InputStream emptyDatabaseStream = database.getClass().
                        getResourceAsStream("/kalkon/defaultFiles/emptyDatabase.xml");

                if(emptyDatabaseStream != null){
                    copy(emptyDatabaseStream, databaseStream);
                    emptyDatabaseStream.close();
                    System.out.println(MessageFormat.format(
                            bundle.getString("empty.database.created"),
                            database.getAbsolutePath()));
                }
                databaseStream.flush();
                databaseStream.close();

            } catch (IOException ex) {
                System.err.println(ex.toString());
                System.exit(FAILURE);
            }
        }

        if(!database.isFile()){
            System.err.println(MessageFormat.format(
                    bundle.getString("not.regular.file"),
                    conf.getDatabaseLocation()));
            System.exit(FAILURE);
        }

        if(!database.canRead() || !database.canWrite()){
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.read.write"),
                    conf.getDatabaseLocation()));
        }

        // file should be OK now

        InputStream repreStream = null;
        try {
            repreStream = new FileInputStream(database);
        } catch (FileNotFoundException ex) {
            System.err.println(ex.toString());
            System.exit(FAILURE);
        }

        InternalRepresentation repre = InternalRepresentation
                .getRepresentation(repreStream);

        if(repre == null){
            System.err.println(bundle.getString("cannot.parse.database"));
            System.exit(FAILURE);
        }

        try {
            repreStream.close();
        } catch (IOException ex) {
            System.err.println(bundle.getString("cannot.close.database"));
            System.exit(FAILURE);
        }

        // execute input modules

        for(InputModule iModule : inputModules){

            System.out.println(MessageFormat.format(
                    bundle.getString("execute.module"),
                    iModule.getClass().getCanonicalName()));

            List<String> clubIDs = iModule.getClubsIDs();

            for(String clubId : clubIDs){

                Document clubInfo = iModule.getClubInfoByID(clubId);
                Document clubEvents = null;
                try {
                    clubEvents = iModule.getClubEventsByID(clubId);
                } catch (InputModuleException ex) {
                    System.err.println(MessageFormat.format(
                            bundle.getString("cannot.get.events"),
                            iModule.getClass().toString()));
                    System.err.println(ex.getMessage());
                    continue;
                }

                if(!repre.hasClub(clubId)){
                    repre.addClub(clubInfo);
                }

                repre.addConcerts(clubId, clubEvents);
            }


        }

        // save internal representation
        OutputStream databaseOutputStream = null;
        try {
            // save internal representation
            databaseOutputStream = new FileOutputStream(database);

        } catch (FileNotFoundException ex) {
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    database.getPath()));
            System.err.println(ex);
            System.exit(FAILURE);
        }

        repre.saveRepresentation(databaseOutputStream);

// Produce output

        System.out.println(bundle.getString("producing.output"));

        OutputModule out = null;

        switch(conf.getOutputMethod()){
            case FTP:
                out = new OutputModuleFTP();
                // TODO: set options

            case LOCAL:
                out = new OutputModuleLocal();
        }

        out.setRootDir(conf.getRootUrl().getPath());

        // Create main page
        Transformer mainPageTransform = Transformer.getTransformer(out.getClass()
                .getResourceAsStream("/kalkon/transformations/basicMainPage.xslt"));
        mainPageTransform.setParameter("url", conf.getApplicationLocation());
        Document mainPage = mainPageTransform.transformToDocument(repre.getDocument());
        OutputStream mainPageOs = out.getOutputStream("index.html");
        
        if(mainPageOs == null){
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    "index.html"));
            System.exit(FAILURE);
        }

        PrintWriter mainPageWriter = new PrintWriter(mainPageOs);
        mainPageWriter.print(mainPage.toXML());
        mainPageWriter.close();
        try {
            mainPageOs.close();
        } catch (IOException ex) {
            System.err.printf(MessageFormat.format(
                    bundle.getString("cannot.save.output"),
                    "index.html"));
            System.exit(FAILURE);
        }

        System.out.println(MessageFormat.format(
                bundle.getString("output.file.written"),
                "index.html"));
        
        // Create css file
        File styleSheet = new File(conf.getRootUrl().getPath()+File.separator+"style.css");
        OutputStream styleSheetOs = null;
        InputStream styleSheetIs = null;
        try {
            styleSheetOs = new FileOutputStream(styleSheet);
            styleSheetIs = styleSheet.getClass().getResourceAsStream("/kalkon/defaultFiles/style.css");
        } catch (FileNotFoundException ex) {
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    styleSheet.getPath()));
            System.err.println(ex);
            System.exit(FAILURE);
        }
        if(styleSheetOs==null){
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    styleSheet.getPath()));
            System.exit(FAILURE);
        }
        if(styleSheetIs == null){
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.read"),
                    "style.css"));
            System.exit(FAILURE);
        }
        try {
            main.copy(styleSheetIs, styleSheetOs);
        } catch (IOException ex) {
           System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    styleSheet.getPath()));
            System.exit(FAILURE);
        }
        try{
            styleSheetIs.close();
            styleSheetOs.close();
        }catch (IOException ex) {
            System.err.printf(MessageFormat.format(
                    bundle.getString("cannot.save.output"),
                    "style.css"));
            System.exit(FAILURE);
        }

        // Create picture for index.html's design
        File picture = new File(conf.getRootUrl().getPath()+File.separator+"cross.png");
        OutputStream pictureOs = null;
        InputStream pictureIs = null;
        try {
            pictureOs = new FileOutputStream(picture);
            pictureIs = picture.getClass().getResourceAsStream("/kalkon/defaultFiles/cross.png");
        } catch (FileNotFoundException ex) {
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    picture.getPath()));
            System.err.println(ex);
            System.exit(FAILURE);
        }
        if(pictureOs==null){
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    "cross.png"));
            System.exit(FAILURE);
        }
        if(pictureIs == null){
            System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.read"),
                    "cross.png"));
            System.exit(FAILURE);
        }
        try {
            main.copy(pictureIs, pictureOs);
        } catch (IOException ex) {
           System.err.println(MessageFormat.format(
                    bundle.getString("cannot.open.write"),
                    "cross.png"));
            System.exit(FAILURE);
        }
        try{
            pictureIs.close();
            pictureOs.close();
        }catch (IOException ex) {
            System.err.printf(MessageFormat.format(
                    bundle.getString("cannot.save.output"),
                    "cross.png"));
            System.exit(FAILURE);
        }

        // Create calendar file for every club
        Transformer concertsTransformer = Transformer.getTransformer(out.getClass()
                .getResourceAsStream("/kalkon/transformations/eventsToIcal.xslt"));

        List<String> clubsIds = repre.getClubsIDs();

        for(String clubId : clubsIds){

            Document concerts = repre.getEventsForId(clubId);

            Text concertOut = concertsTransformer.transformToText(concerts);

            String clubFilename = clubId + ".ical";
            OutputStream os = out.getOutputStream(clubFilename);
            if (os == null) {
                System.err.println(MessageFormat.format(
                        bundle.getString("cannot.open.write"),
                        clubFilename));
                System.exit(FAILURE);
            }
            PrintWriter writer = new PrintWriter(os);
            writer.print(concertOut.toXML());
            writer.close();
            try {
                os.close();
            } catch (IOException ex) {
                System.err.printf(MessageFormat.format(
                        bundle.getString("cannot.save.output"),
                        clubFilename));
            }
            System.out.println(MessageFormat.format(
                bundle.getString("output.file.written"),
                clubFilename));
        }

        System.out.println(bundle.getString("done"));
        
       }
}
